/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package projorientado.application;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import javax.imageio.ImageIO;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;
import projorientado.DebugVisitorAnpr;
import projorientado.DebugVisitorComparator;
import projorientado.anpr.OCRPlate;
import projorientado.anpr.PlateRecognitionResult;
import projorientado.anpr.PlateRecognizer;
import projorientado.anpr.impl.BPlateRecognizer;
import projorientado.anpr.impl.BPlateRecognizerBlock;
import projorientado.anpr.impl.BPlateRecognizerProps;
import projorientado.anpr.impl.FormatedOCRPlate;
import projorientado.image.ImageLayerData;
import projorientado.ocr.OCRecognizer;
import projorientado.ocr.impl.AlphabetChar;
import projorientado.ocr.impl.SimpleRecognizer;
import projorientado.ocr.vfeature.FeaturesComparator;
import projorientado.ocr.vfeature.FeaturesExtractor;
import projorientado.ocr.vfeature.FeaturesPairExtractor;
import projorientado.ocr.vfeature.impl.ImageFeaturesComparator;
import projorientado.ocr.vfeature.impl.ImagePairExtractor;
import projorientado.ocr.vfeature.impl.comparator.CorrelationComparator;
import projorientado.ocr.vfeature.impl.comparator.EuclideanComparator;

/**
 *
 * @author João Neto
 */
public class Reconhecedor {

    private ResourceMap resourceMap;
    private static Reconhecedor instance;
    private PlateRecognizer anpr;
    private long tempoDaUltimaLeitura;
    private String alphabetDir;
    private boolean debugging;
    private String debugVisitorDir;
    private DebugVisitorAnpr visitor;
    private Vector<AlphabetChar> alphabet;
    private OCRecognizer ocr;
    

    private Reconhecedor() throws IOException {
        resourceMap = Application.getInstance(projorientado.application.Main.class)
                .getContext().getResourceMap(Reconhecedor.class);
        alphabetDir = resourceMap.getString("PlateRecognizer.alphabetDir");
        alphabet = createAlphabet();
        debugVisitorDir = resourceMap.getString("PlateRecognizer.debugVisitorDir");
        visitor = new DebugVisitorAnpr(debugVisitorDir);
        debugging = false;
        ocr = createOCRecognizer(null);
        anpr = createBRPlateRecognizer(new FormatedOCRPlate(ocr,resourceMap
                .getString("PlateRecognizer.plateFormat")));
    }

    public Vector<String> reconhecerPlacas(BufferedImage imagem) throws IOException, NoPlatesFoundException {
        Vector<String> result = new Vector<String>();
        PlateRecognitionResult pRResult;
        if (debugging){
            clearFolder(new File(debugVisitorDir));
            pRResult = anpr.recognizePlate(imagem,visitor);
        } else  {
            pRResult = anpr.recognizePlate(imagem);
        }

        tempoDaUltimaLeitura = pRResult.getTime();
        if (pRResult.getPlates() != null) {
            for(int i = 0; i < pRResult.getPlates().length; i++) {
                    result.add(pRResult.getPlates()[i].getPlateString());
            }
        } else {
            throw new NoPlatesFoundException("Não foram encontradas placas");
        }
        return result;
    }

    public static Reconhecedor getInstance() throws IOException {
        if (instance == null) instance = new Reconhecedor();
        return instance;
    }


    private PlateRecognizer createBRPlateRecognizer(OCRPlate ocrp) {
        BPlateRecognizerProps props = new BPlateRecognizerProps();

        props.plateCharWidthMax = resourceMap.getInteger("PlateRecognizer.plateCharWidthMax");
        props.plateCharWidthMin = resourceMap.getInteger("PlateRecognizer.plateCharWidthMin");

        props.plateCharHeightMax = resourceMap.getInteger("PlateRecognizer.plateCharHeightMax");
        props.plateCharHeightMin = resourceMap.getInteger("PlateRecognizer.plateCharHeightMin");

        props.plateVertMaxDiff = resourceMap.getInteger("PlateRecognizer.plateVertMaxDiff");
        props.normalizeThresholding = resourceMap.getFloat("PlateRecognizer.normalizeThresholding");
        props.adptiveThresholdingRadius = resourceMap.getInteger("PlateRecognizer.adptiveThresholdingRadius");

        props.plateHorzMaxDiff = resourceMap.getInteger("PlateRecognizer.plateHorzMaxDiff");
        props.plateHorzMinDiff = resourceMap.getInteger("PlateRecognizer.plateHorzMinDiff");
        
        props.improveLinearPoint = resourceMap.getFloat("PlateRecognizer.improveLinearPoint");
        props.improveLinearDiff = resourceMap.getFloat("PlateRecognizer.improveLinearDiff");
        
        props.improveSin = resourceMap.getFloat("PlateRecognizer.improveSin");

        BPlateRecognizerBlock block1 = new BPlateRecognizerBlock();

        block1.numSegments = resourceMap.getInteger("PlateRecognizerBlock1.numSegments");
        block1.maxWidthDiffNextBlock = resourceMap.getInteger("PlateRecognizerBlock1.maxWidthDiffNextBlock");
        block1.minWidthDiffNextBlock = resourceMap.getInteger("PlateRecognizerBlock1.minWidthDiffNextBlock");

        props.blocks.add(block1);

        BPlateRecognizerBlock block2 = new BPlateRecognizerBlock();

        block2.numSegments = resourceMap.getInteger("PlateRecognizerBlock2.numSegments");

        props.blocks.add(block2);

        PlateRecognizer recognizer = new BPlateRecognizer(ocrp,props);

        return recognizer;
    }

    private Vector<AlphabetChar> createAlphabet() throws IOException {
        Vector<AlphabetChar> alphabet = new Vector();
        String alphabetKeys = resourceMap.getString("PlateRecognizer.alphabetKeys");
        String alphabetPrefix = resourceMap.getString("PlateRecognizer.alphabetPrefix");
        String alphabetSulfix = resourceMap.getString("PlateRecognizer.alphabetSulfix");
        for(int i = 0; i < alphabetKeys.length(); i++) {
            char c = alphabetKeys.charAt(i);
            AlphabetChar ac = new AlphabetChar();
            ac.setCharValue(c);

            File af = new File(alphabetDir+alphabetPrefix+c+alphabetSulfix);
            BufferedImage abic = ImageIO.read(af);
            ImageLayerData iac = ImageLayerData.extractBrightnessLayer(abic);

            //iac = iac.adaptiveThresholding(5);
            iac = iac.trim(0.5f);

            ac.setImage(iac);

            alphabet.add(ac);
        }
        return alphabet;
    }

    private OCRecognizer createOCRecognizer(String debugVisitorDir) {     
        FeaturesPairExtractor pairExtractor =
                new ImagePairExtractor(FeaturesExtractor.PointExtractor.instanceResizer,ImagePairExtractor.MAX_SIZE);
        FeaturesComparator comparator = CorrelationComparator.instance;

        SimpleRecognizer recognizer = new SimpleRecognizer(alphabet,
                new ImageFeaturesComparator(
                    pairExtractor,
                    comparator));

        if(debugVisitorDir != null) {
            DebugVisitorComparator debugVisitor = new DebugVisitorComparator(debugVisitorDir);

            ((ImageFeaturesComparator)recognizer.getImageComparator()).setVisitor(debugVisitor);
        }

        return recognizer;
    }

    public static void clearFolder(File folder) {
        File[] toDel = folder.listFiles();
        for(int i = 0; i < toDel.length; i++) {
            if(toDel[i].isDirectory()) {
                clearFolder(toDel[i]);
                toDel[i].delete();
            } else
                toDel[i].delete();
        }
    }

    /**
     * @return the tempoDaUltimaLeitura
     */
    public long getTempoDaUltimaLeitura() {
        return tempoDaUltimaLeitura;
    }

    /**
     * @return the isDebugging
     */
    public boolean isDebugging() {
        return debugging;
    }

    /**
     * @param isDebugging the isDebugging to set
     */
    public void setDebugging(boolean isDebugging) {
        if (debugging != isDebugging) {
            debugging = isDebugging;
            if (debugging)
                ocr = createOCRecognizer(debugVisitorDir);
            else
                ocr = createOCRecognizer(null);
            anpr = createBRPlateRecognizer(new FormatedOCRPlate(ocr,resourceMap
                .getString("PlateRecognizer.plateFormat")));
        }
    }

}
